<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <title>cannon.js - trimesh demo</title>
    <link rel="stylesheet" href="css/style.css" type="text/css" />
    <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
  </head>
  <body>
    <script type="module">
      import * as CANNON from '../dist/cannon-es.js'
      import { Demo } from './js/Demo.js'

      const demo = new Demo()

      demo.addScene('Raycasting', () => {
        const world = demo.getWorld()
        world.gravity.set(0, 0, 0)

        // Particle as marker for the raycast hit
        const N = 10
        const particleShape = new CANNON.Particle()
        const particleBodies = []
        for (let i = 0; i < N * N; i++) {
          const particleBody = new CANNON.Body({
            mass: 1,
            shape: particleShape,
            collisionResponse: false,
          })
          world.addBody(particleBody)
          demo.addVisual(particleBody)
          particleBodies.push(particleBody)
        }

        // Torus
        const torusShape = CANNON.Trimesh.createTorus(4, 3.5, 16, 16)
        const torusBody = new CANNON.Body({ mass: 1 })
        torusBody.addShape(torusShape)
        torusBody.position.set(0.01, 0.01, 0.01)
        torusBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        torusBody.angularVelocity.set(0, 0, 1)
        world.addBody(torusBody)
        demo.addVisual(torusBody)

        // Do the raycasting
        const from = new CANNON.Vec3(-10, 0, 1)
        const to = new CANNON.Vec3(10, 0, 1)
        const result = new CANNON.RaycastResult()
        const raycastOptions = {}
        function postStepListener(event) {
          for (let i = 0; i < N; i++) {
            for (let j = 0; j < N; j++) {
              from.set(-10, i * 0.1, j * 0.1)
              to.set(10, i * 0.1, j * 0.1)
              result.reset()
              world.raycastClosest(from, to, raycastOptions, result)
              particleBodies[i * N + j].position.copy(result.hitPointWorld)
            }
          }
        }

        world.addEventListener('postStep', postStepListener)

        // Remove listener when we change demo
        demo.addEventListener('destroy', () => {
          world.removeEventListener('postStep', postStepListener)
        })
      })

      demo.addScene('Trimesh', () => {
        const world = setupWorld(demo)

        // Sphere
        const sphereShape = new CANNON.Sphere(1)
        const sphereBody = new CANNON.Body({
          mass: 1,
          shape: sphereShape,
          position: new CANNON.Vec3(-3, 11, 3),
        })
        world.addBody(sphereBody)
        demo.addVisual(sphereBody)

        // Torus
        const torusShape = CANNON.Trimesh.createTorus(4, 3.5, 16, 16)
        const torusBody = new CANNON.Body({ mass: 1 })
        torusBody.addShape(torusShape)
        torusBody.position.set(0, 4, 0)
        torusBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        torusBody.velocity.set(0, 1, 1)
        world.addBody(torusBody)
        demo.addVisual(torusBody)
      })

      demo.start()

      function setupWorld(demo) {
        const world = demo.getWorld()
        world.gravity.set(0, -10, 0)

        // Tweak contact properties.
        // Contact stiffness - use to make softer/harder contacts
        world.defaultContactMaterial.contactEquationStiffness = 1e7

        // Stabilization time in number of timesteps
        world.defaultContactMaterial.contactEquationRelaxation = 4

        // Static ground plane
        const groundShape = new CANNON.Plane()
        const groundBody = new CANNON.Body({ mass: 0 })
        groundBody.addShape(groundShape)
        groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
        world.addBody(groundBody)
        demo.addVisual(groundBody)

        return world
      }
    </script>
  </body>
</html>
